package com.northpool.operator.common.s3;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.northpool.operator.annotation.InputParam;
import com.northpool.operator.annotation.InputParams;
import com.northpool.operator.annotation.OperatorInfo;
import com.northpool.operator.annotation.OutputParam;
import com.northpool.operator.annotation.OutputParams;
import com.northpool.operator.common.algorithm.OperatorTestBuild;
import com.northpool.operator.common.algorithm.TestParamBuilder;
import com.northpool.operator.common.minio.FileObject;
import com.northpool.operator.common.minio.MinIOUtil;
import com.northpool.operator.param.ParamTool;
import com.northpool.operator.type.TypeEnum;
import com.northpool.operator.utils.StpOperatorHelper;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.InsufficientDataException;
import io.minio.errors.InternalException;
import io.minio.errors.InvalidResponseException;
import io.minio.errors.ServerException;
import io.minio.errors.XmlParserException;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

@OperatorInfo(ref = "S3FileCompressOperator", name = "对象存储文件压缩", description = "对象存储文件压缩，压缩后重新放到对象存储，并提供下载地址")
@OutputParams({
        @OutputParam(ref = "url", name = "下载地址", desc = "文件下载地址")
})
public class S3FileCompressOperator extends StpOperatorHelper {

    @InputParams({
            @InputParam(ref = "filePath", name = "文件路径", desc = "待压缩的路径, 如: apps/supermap/", required = true),
            @InputParam(ref = "zipPath", name = "压缩文件路径", desc = "压缩后的路径,存储在桶内, 如: backup/apps.zip", required = true),
            @InputParam(ref = "minioEndpoint", name = "对象存储地址", desc = "http://ip:port", required = true),
            @InputParam(ref = "bucket", name = "桶名称", desc = "桶名称, 如: stp-minio", required = true),
            @InputParam(ref = "accessKey", name = "minio令牌", desc = "minio令牌", type = TypeEnum.STRING, required = true),
            @InputParam(ref = "secretKey", name = "minio密钥", desc = "minio密钥", type = TypeEnum.STRING, required = true),
            @InputParam(ref = "urlPrefix", name = "下载地址路径前缀", desc = "下载地址路径前缀,适用于对象存储地址对外不可访问的情况, 如: http://192.168.1.1:8021/stp-minio", type = TypeEnum.STRING, required = true),
    })
    public S3FileCompressOperator() {
    }

    public static void main(String[] args) throws Exception {
        OperatorTestBuild.execute(S3FileCompressOperator.class, TestParamBuilder.aBuilder()
                .param("filePath", "apps/supermap/")
                .param("zipPath", "backup/apps.zip")
                .param("minioEndpoint", "http://northpool.work:9000")
                .param("bucket", "stp-minio")
                .param("accessKey", "northpool")
                .param("secretKey", "piesat@123")
                .param("urlPrefix", "http://northpool.work:9000/stp-minio")
                .build());
    }

    @Override
    public void execute(ParamTool paramTool) throws Exception {
        String zipPath = paramTool.getStr("zipPath");
        String path = paramTool.getStr("filePath").trim();
        String minioEndpoint = paramTool.getStr("minioEndpoint");
        String bucket = paramTool.getStr("bucket");
        String accessKey = paramTool.getStr("accessKey");
        String secretKey = paramTool.getStr("secretKey");
        String urlPrefix = paramTool.getStr("urlPrefix");

        String srcPath = "";
        boolean isDownload = false;
        File zip = null;
        try {
            MinIOUtil minIOUtil = new MinIOUtil(minioEndpoint, accessKey, secretKey);
            if (!minIOUtil.bucketExists(bucket)) {
                throw new RuntimeException("桶不存在：" + bucket);
            }
            File workerDir = this.workerDir();
            // 下载
            dpDownload(minIOUtil, workerDir, bucket, path);
            // 压缩
            srcPath = workerDir + File.separator + path;
            isDownload = true;
            zip = ZipUtil.zip(srcPath, StandardCharsets.UTF_8);
            BufferedInputStream inputStream = FileUtil.getInputStream(zip);
            minIOUtil.putObject(bucket, zipPath, inputStream);
            log("上传完成: {}", path);

            String url = StrUtil.format("{}/{}", urlPrefix, zipPath);
            this.result("url", url);
        } catch (Exception ex) {
            this.error("压缩对象存储文件包失败: {}", ex.getMessage());
            this.error(ExceptionUtil.stacktraceToString(ex));
        } finally {
            // 删除临时压缩包和文件
            if (isDownload) {
                FileUtil.del(srcPath);
                if (zip != null) {
                    FileUtil.del(zip);
                }
            }
        }
    }

    private void dpDownload(MinIOUtil minIOUtil, File workerDir, String bucket, String path) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        for (FileObject obj : minIOUtil.treeObjects(bucket, path)) {
            Boolean dir = obj.getDir();
            if (dir) {
                FileUtil.mkdir(workerDir + File.separator + obj.getObjectName());
                for (FileObject child : obj.getChildren()) {
                    dpDownload(minIOUtil, workerDir, bucket, obj.getObjectName());
                }
            } else {
                InputStream inputStream = minIOUtil.getObject(bucket, obj.getObjectName());
                String targetPath = workerDir + File.separator + obj.getObjectName();
                FileUtil.writeFromStream(inputStream, targetPath);
            }
        }
    }
}
